package org.jflame.commons.convert;

import org.jflame.commons.reflect.ReflectionHelper;
import org.jflame.commons.util.CollectionHelper;

import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.reflect.TypeUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;

/**
 * 类型转换注册器,注册一组类型转换器用于自动转换
 * 
 * @author charles.zhang
 */
public class ConverterRegistry {

    private static ConverterRegistry defaultConverterRegistry = new ConverterRegistry();

    private Map<ImmutablePair<Class<?>,Class<?>>,Converter<?,?>> converterMap = new LinkedHashMap<>();

    /**
     * 注册一个类型转换器,根据泛型参数自动获取可转换的类型
     * 
     * @param converter
     */
    public void addConverter(Converter<?,?> converter) {
        // 按接口方法提供的可转换类型注册
        if (converter instanceof ConvertibleType) {
            Set<ImmutablePair<Class<?>,Class<?>>> convertibleTypes = ((ConvertibleType) converter)
                    .getConvertibleTypes();
            if (CollectionHelper.isNotEmpty(convertibleTypes)) {
                for (ImmutablePair<Class<?>,Class<?>> pair : convertibleTypes) {
                    converterMap.put(pair, converter);
                }
                return;
            }
        }
        // 通过反射获取泛型参数类型注册
        Type[] types = ReflectionHelper.getInterfaceGenericTypes(converter.getClass());
        if (types != null) {
            // 只有一个泛型参数,是有多层级继承
            if (types.length == 1) {
                Class<?> sourceClass = null,targetClass = null;
                Map<TypeVariable<?>,Type> typeMap = TypeUtils.getTypeArguments(converter.getClass(), Converter.class);
                for (Map.Entry<TypeVariable<?>,Type> entry : typeMap.entrySet()) {
                    if (entry.getKey()
                            .getName()
                            .equals("S")) {
                        if (entry.getValue() instanceof Class<?>) {
                            sourceClass = (Class<?>) entry.getValue();
                        }
                    } else if (entry.getKey()
                            .getName()
                            .equals("T")) {
                        if (entry.getValue() instanceof Class<?>) {
                            targetClass = (Class<?>) entry.getValue();
                        }
                    }
                }
                if (sourceClass != null && targetClass != null) {
                    converterMap.put(ImmutablePair.of(sourceClass, targetClass), converter);
                    return;
                }
            } else if (types.length == 2) {
                converterMap.put(ImmutablePair.of((Class<?>) types[0], (Class<?>) types[1]), converter);
                return;
            }
        }
        throw new IllegalArgumentException("Unable to determine source type <S> and target type <T> for your "
                + "Converter: " + converter.getClass()
                        .getName());
    }

    /**
     * 注册类型转换器,并指定转换器可转换的类型,无法通过解析泛型参数来获取注册类型时使用该方法指定
     * 
     * @param <S>
     * @param <T>
     * @param converter 转换器实例
     * @param sourceType 待转换类型Class
     * @param targetType 目标类型Class
     */
    public <S,T> void addConverter(Converter<? super S,? extends T> converter, Class<S> sourceType,
            Class<T> targetType) {
        converterMap.put(ImmutablePair.of(sourceType, targetType), converter);
    }

    @SuppressWarnings("unchecked")
    public <S,T> Optional<Converter<S,T>> getConverter(Class<S> sourceType, Class<T> targetType) {
        Converter<S,T> converter = (Converter<S,T>) converterMap.get(ImmutablePair.of(sourceType, targetType));
        if (converter == null && ClassUtils.isPrimitiveWrapper(sourceType)) {
            converter = (Converter<S,T>) converterMap
                    .get(ImmutablePair.of(ClassUtils.primitiveToWrapper(sourceType), targetType));
        }
        if (converter == null && this != ConverterRegistry.defaultConverterRegistry) {
            return ConverterRegistry.getDefaultConverter(sourceType, targetType);
        }
        return Optional.ofNullable(converter);
    }

    /**
     * 从默认类型转换器里获取转换器
     * 
     * @param <S>
     * @param <T>
     * @param sourceType
     * @param targetType
     * @throws ConvertException
     * @return
     */
    public static <S,T> Optional<Converter<S,T>> getDefaultConverter(Class<S> sourceType, Class<T> targetType) {
        return ConverterRegistry.defaultConverterRegistry.getConverter(sourceType, targetType);
    }

    /**
     * 返回默认转换器注册器
     * 
     * @return
     */
    public static ConverterRegistry getDefaultConverterRegistry() {
        return defaultConverterRegistry;
    }

    static {
        // 初始注册常用转换器

        defaultConverterRegistry.addConverter(new StringToBoolConverter());
        defaultConverterRegistry.addConverter(new DateToStringConverter());
        defaultConverterRegistry.addConverter(new TemporalToStringConverter());
        defaultConverterRegistry.addConverter(new NumberToStringConverter());

        defaultConverterRegistry.addConverter(new StringToDateConverter<>(Date.class));
        defaultConverterRegistry.addConverter(new StringToDateConverter<>(java.sql.Date.class));
        defaultConverterRegistry.addConverter(new StringToDateConverter<>(java.sql.Timestamp.class));
        defaultConverterRegistry.addConverter(new StringToDateConverter<>(java.sql.Time.class));

        defaultConverterRegistry.addConverter(new StringToTemporalConverter<>(LocalDate.class));
        defaultConverterRegistry.addConverter(new StringToTemporalConverter<>(LocalDateTime.class));
        defaultConverterRegistry.addConverter(new StringToTemporalConverter<>(LocalTime.class));

        defaultConverterRegistry.addConverter(new StringToNumberConverter<>(Byte.class));
        defaultConverterRegistry.addConverter(new StringToNumberConverter<>(Short.class));
        defaultConverterRegistry.addConverter(new StringToNumberConverter<>(Integer.class));
        defaultConverterRegistry.addConverter(new StringToNumberConverter<>(Long.class));
        defaultConverterRegistry.addConverter(new StringToNumberConverter<>(Double.class));
        defaultConverterRegistry.addConverter(new StringToNumberConverter<>(Float.class));
        defaultConverterRegistry.addConverter(new StringToNumberConverter<>(BigDecimal.class));

    }

}
